package com.ruoyi.web.controller.me;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.exception.ProcessStopException;
import com.ruoyi.common.utils.ExecUtil;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.SseEmitterUtils;
import com.ruoyi.common.utils.biz.BizModule;
import com.ruoyi.common.utils.biz.CommonUtil;
import com.ruoyi.common.utils.biz.PathUtil;
import com.ruoyi.common.utils.biz.ResultSaveModule;
import com.ruoyi.dm.mapper.DataCollectionMapper;
import com.ruoyi.framework.datasource.DynamicDataSource;
import com.ruoyi.web.vo.DifferentSpaceVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.File;
import java.sql.Connection;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <p>
 *  不同空间环境条件
 * </p>
 *
 * @author liuhl
 * @since 2024-05-13
 */
@RestController
@RequestMapping("/me/differentSpace")
@AllArgsConstructor
@Slf4j
@Api(tags = "模型检验评估-不同空间环境条件")
public class DifferentSpaceController {

    private DynamicDataSource dataSource;
    private DataCollectionMapper dataCollectionMapper;



    @PostMapping("/timeList")
    @ApiOperation(value = "评估时间下拉列表", notes = "获取评估时间下拉列表", httpMethod = "POST")
    public AjaxResult timeList(@RequestBody DifferentSpaceVo vo) {

        String codePath = PathUtil.codeModuleDir(BizModule.MODEL_ASSESS)  + PathUtil.moduleDirs().getModelAssess().get("space_time");
        String tmpDir = PathUtil.tmpModuleDir(BizModule.MODEL_ASSESS);

        //python /dgdq/code/dgdq-algorithm/modelAssess/different_space_environment/Cal_eval_time.py
        // 90 150 15 40 2016
        // mid_solar_activity
        // periods_of_severe_magnetic_storms_time  /dgdq/tmp/modelAssess
        String[] solarRange = vo.getSolarRange().split(",");
        String[] magRange = vo.getMagRange().split(",");

        String command = "python "+codePath+" "+solarRange[0]+" "+solarRange[1]+" "
                + magRange[0] + " " + magRange[1] + " "
                + vo.getYear() + " "
                + vo.getSolarLevel() + " "
                + vo.getMagLevel() + " "
                + tmpDir;

        String results = ExecUtil.execCmdWithResult(command);
        if (results == null || !results.contains("###")){
            return AjaxResult.error(results);
        }
        String timeListStr = results.split("###")[1];
        return AjaxResult.success(JSONUtil.parseArray(timeListStr));
    }

    @SneakyThrows
    @PostMapping("/sateList")
    @ApiOperation(value = "评估卫星下拉列表", notes = "获取评估卫星下拉列表", httpMethod = "POST")
    public AjaxResult sateList(@RequestBody DifferentSpaceVo vo) {

        //	python /dgdq/code/dgdq-algorithm/modelAssess/different_space_environment/get_statellite_list_new.py
        //	2015-03-07 420 500
        //	'SWDATA_OBS_DEN_CHAMP,SWDATA_OBS_DEN_GOCE,SWDATA_OBS_DEN_GRACEA,SWDATA_OBS_DEN_GRACEB,SWDATA_OBS_DEN_GRACEFO,SWDATA_OBS_DEN_SWARMA,SWDATA_OBS_DEN_SWARMB,SWDATA_OBS_DEN_SWARMC'
        // 获取模式名
        String schemaName;
        Connection connection = null;
        try {
            connection = dataSource.getConnection();
            String schema = connection.getSchema();
            if (StrUtil.isNotBlank(schema)) {
                schemaName = schema;
            } else {
                schemaName = connection.getMetaData().getUserName();
            }
            List<String> tableNames = dataCollectionMapper.selectAllTableNames(schemaName.toLowerCase()); //大写最好
            if (CollUtil.isEmpty(tableNames)) {
                return AjaxResult.error("模式: {} 下没有找到任何数据库表");
            }

            String tableNameStr = tableNames
                    .stream()
                    .map(String::toUpperCase)
                    .filter(name -> name.contains("SWDATA_OBS_DEN_"))
                    .filter(name -> !name.contains("GRACEA_SZ2") && !name.contains("GRACEA_GRACEB"))
                    .collect(Collectors.joining(","));

            String codePath = PathUtil.codeModuleDir(BizModule.MODEL_ASSESS) + PathUtil.moduleDirs().getModelAssess().get("space_sate");
            String[] altArr = vo.getAltitude().split(",");
            String command = "python " + codePath + " "
                    + vo.getAssessTime() + " "
                    + altArr[0] + " "
                    + altArr[1] + " "
                    + tableNameStr;
            String results = ExecUtil.execCmdWithResult(command);
            if (results == null || !results.contains("###")) {
                return AjaxResult.error(results);
            }
            String jsonStr = results.split("###")[1];
            Map<String, Object> altAndTableNameMap = JSONUtil.toBean(jsonStr, Map.class);
            Map<String, Object> resultMap = altAndTableNameMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> {
                String nameArr = entry.getValue().toString(); // ["SWDATA_OBS_DEN_SWARMB"]
                if (nameArr.contains("None")){
                    return "None";
                }
                return JSONUtil.parseArray(nameArr).toList(String.class)
                        .stream()
                        .map(name -> {
                            return MapUtil.builder(new HashMap<>())
                                    .put("label", name.replace("SWDATA_OBS_DEN_", ""))
                                    .put("value", name)
                                    .build();
                        }).collect(Collectors.toList());
            }));

            return AjaxResult.success(resultMap);
        } catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.error(e.getMessage());
        }finally {
            if (null != connection) {
                connection.close();
            }
        }
    }


    @PostMapping("/cal")
    @ApiOperation(value = "计算评估", notes = "根据时间、位置、模型进行计算评估", httpMethod = "POST")
    public AjaxResult calculate(@Validated @RequestBody DifferentSpaceVo vo) throws ProcessStopException {

        //	python /dgdq/code/dgdq-algorithm/modelAssess/different_space_environment/different_Space_environment_eval_new.py
        //	2015-03-07 0~420:SWDATA_OBS_DEN_GRACEA,421~500:SWDATA_OBS_DEN_GRACEB,501~morethan:SWDATA_OBS_DEN_SWARMB
        //	360 JB2008,nrlmsise00 /dgdq/tmp/modelAssess
        //

        String codePath = PathUtil.codeModuleDir(BizModule.MODEL_ASSESS)  + PathUtil.moduleDirs().getModelAssess().get("space_cal");
        String tmpDir = PathUtil.tmpModuleDir(BizModule.MODEL_ASSESS);

        String time = vo.getAssessTime();
        String sates = vo.getSatellite();
        String models = String.join(",",vo.getModel());

        List<String> command = Stream.of("python",
                "-u",
                codePath,
                time,
                sates,
                vo.getInterval(),
                models,
                tmpDir
        ).collect(Collectors.toList());
        String sseId = vo.getSseId()+SecurityUtils.getLoginUser().getUsername();
        String results = ExecUtil.execPython(command, sseId);

        if (results == null || !results.contains("###")){
            return AjaxResult.error(results);
        }
        String txtPath = results.split("###")[1];

        String content = CommonUtil.readStrData(new File(txtPath), null);
        Map<String, JSONObject> contentMap = JSONUtil.toBean(content, Map.class);

        List<String> altNameList = Arrays.asList(vo.getSatellite().split(","))
                .stream()
                .map(o -> o.split(":")[0].replace("morethan", "以上") + "km")
                .collect(Collectors.toList());

        Map<String,Object> resultMap = new HashMap<>();

        //1.观测值和参评模型模拟值分布结果图
        Map<String,Object> simuMap = new HashMap<>();
        Map<String,Object> altAndTimeListMap = new HashMap<>();

        Map<String,Object> modelDataMap = (Map<String,Object>)contentMap.get("modelData");

        altNameList.forEach(altName -> {
            Map<String,Object> simuDataMap = (Map<String,Object>)modelDataMap.get(altName + "_Simu");
            Object obsData = modelDataMap.get(altName + "_Density");
            Object timeList = modelDataMap.get(altName + "_Time");

            simuDataMap.put("obs",obsData);
            simuDataMap.put("time",timeList);

            altAndTimeListMap.put(altName, timeList); //顺便记录下时间数组，后面要用
            simuMap.put(altName, simuDataMap);
        });
        resultMap.put("simu", simuMap);

        Map<String,Object> errorDataMap = (Map<String,Object>) contentMap.get("errorData");

        //2.收集 图2 、图3 所需的数据信息

        Map<String,Map<String,Map<String,Object>>> absoluteRelativeMap = new HashMap<>();
        Map<String,Map<String,Object>> coeeMap = new HashMap<>();

        // 这三个errorType都是 模型、站点、resultType
        Map<String,Map<String,Map<String,Object>>> rootMap = new HashMap<>();
        Map<String,Map<String,Map<String,Object>>> absoluteErrorMap = new HashMap<>();
        Map<String,Map<String,Map<String,Object>>> relativeErrorMap = new HashMap<>();

        errorDataMap.entrySet().forEach(entry -> {
            String key = entry.getKey();
            Object value = entry.getValue();

            //收集图2 所需的数据信息
            if (key.contains("absolute_error#result") || key.contains("relative_error#result")){
                //"0-400km#JB2008#relative_error#result"
                key = key.replace("_error#result", "");
                String[] mapKeyArr = key.split("#");
                String altName = mapKeyArr[0];
                String model = mapKeyArr[1];
                String errorType = mapKeyArr[2];

                Map<String, Map<String, Object>> errorAndModelMap = absoluteRelativeMap.get(altName);
                if (CollUtil.isEmpty(errorAndModelMap)){
                    errorAndModelMap = new HashMap<>();
                    Map<String, Object> dataMap = new HashMap<>();
                    dataMap.put(model, value);
                    dataMap.put("time", altAndTimeListMap.get(altName));
                    errorAndModelMap.put(errorType, dataMap);
                    absoluteRelativeMap.put(altName, errorAndModelMap);
                }else {
                    Map<String, Object> dataMap = errorAndModelMap.get(errorType);
                    if (CollUtil.isEmpty(dataMap)){
                        dataMap = new HashMap<>();
                    }
                    dataMap.put(model, value);
                    dataMap.put("time", altAndTimeListMap.get(altName));
                    errorAndModelMap.put(errorType, dataMap);
                    absoluteRelativeMap.put(altName, errorAndModelMap);
                }
                return;
            }

            //收集图3 和 表格 所需的数据信息
            // key = "0-400km#nrlmsise00#absolute_error#confidenceInterval"
            String[] mapKeyArr = key.split("#");
            String altName = mapKeyArr[0];
            String model = mapKeyArr[1];
            String errorType = mapKeyArr[2];

            if ("corrCoee".equalsIgnoreCase(errorType)){ // "0-400km#JB2008#corrCoee" 相关系数是3层，所以就一个简单MAP
                Map<String, Object> altAndCoee = coeeMap.get(model);
                if (CollUtil.isEmpty(altAndCoee)){
                    altAndCoee = new HashMap<>();
                }
                altAndCoee.put(altName, value);
                coeeMap.put(model, altAndCoee); //前端希望是个数组
                return;
            }

            String resultType = mapKeyArr[3];

            // 这三个errorType都是 模型、站点、resultType
            if ("root_mean_square_error".equalsIgnoreCase(errorType)){
                Map<String, Map<String, Object>> altAndResultMap = rootMap.get(model);
                if (CollUtil.isEmpty(altAndResultMap)){
                    altAndResultMap = new HashMap<>();
                    Map<String, Object> dataMap = new HashMap<>();
                    dataMap.put(resultType, value);
                    altAndResultMap.put(altName, dataMap);
                    rootMap.put(model, altAndResultMap);
                }else {
                    Map<String, Object> dataMap = altAndResultMap.get(altName);
                    if (CollUtil.isEmpty(dataMap)){
                        dataMap = new HashMap<>();
                    }
                    dataMap.put(resultType, value);
                    altAndResultMap.put(altName, dataMap);
                    rootMap.put(model, altAndResultMap);
                }
            }

            if ("relative_error".equalsIgnoreCase(errorType)){
                Map<String, Map<String, Object>> altAndResultMap = relativeErrorMap.get(model);
                if (CollUtil.isEmpty(altAndResultMap)){
                    altAndResultMap = new HashMap<>();
                    Map<String, Object> dataMap = new HashMap<>();
                    dataMap.put(resultType, value);
                    altAndResultMap.put(altName, dataMap);
                    relativeErrorMap.put(model, altAndResultMap);
                }else {
                    Map<String, Object> dataMap = altAndResultMap.get(altName);
                    if (CollUtil.isEmpty(dataMap)){
                        dataMap = new HashMap<>();
                    }
                    dataMap.put(resultType, value);
                    altAndResultMap.put(altName, dataMap);
                    relativeErrorMap.put(model, altAndResultMap);
                }
            }

            if ("absolute_error".equalsIgnoreCase(errorType)){
                Map<String, Map<String, Object>> altAndResultMap = absoluteErrorMap.get(model);
                if (CollUtil.isEmpty(altAndResultMap)){
                    altAndResultMap = new HashMap<>();
                    Map<String, Object> dataMap = new HashMap<>();
                    dataMap.put(resultType, value);
                    altAndResultMap.put(altName, dataMap);
                    absoluteErrorMap.put(model, altAndResultMap);
                }else {
                    Map<String, Object> dataMap = altAndResultMap.get(altName);
                    if (CollUtil.isEmpty(dataMap)){
                        dataMap = new HashMap<>();
                    }
                    dataMap.put(resultType, value);
                    altAndResultMap.put(altName, dataMap);
                    absoluteErrorMap.put(model, altAndResultMap);
                }
            }
        });

        //3.大气模型误差结果统计图
        resultMap.put("absolute_relative",absoluteRelativeMap);
        //4.大气模型误差评估结果统计图
        Map<String, Object> resultErrorMap = new HashMap<>();
        resultErrorMap.put("x", altNameList);

        //<model,<location,resultType>>
        Map<String, List<Object>> avg_absolute = absoluteErrorMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> {
            return altNameList.stream().map(altName -> {
                return absoluteErrorMap.get(entry.getKey()).get(altName).get("averageResult");
            }).collect(Collectors.toList());
        }));

        Map<String, List<Object>> avg_relative = relativeErrorMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> {
            return altNameList.stream().map(altName -> {
                return relativeErrorMap.get(entry.getKey()).get(altName).get("averageResult");
            }).collect(Collectors.toList());
        }));

        Map<String, List<Object>> coee = coeeMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> {
            return altNameList.stream().map(altName -> {
                return coeeMap.get(entry.getKey()).get(altName);
            }).collect(Collectors.toList());
        }));

        Map<String, List<Object>> root = rootMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> {
            return altNameList.stream().map(altName -> {
                return rootMap.get(entry.getKey()).get(altName).get("confidenceInterval");
            }).collect(Collectors.toList());
        }));

        resultErrorMap.put("avg_absolute", avg_absolute);
        resultErrorMap.put("avg_relative", avg_relative);
        resultErrorMap.put("coee", coee);
        resultErrorMap.put("root", root);

        resultMap.put("error_data",resultErrorMap);

        //表格开始
        List<Map<String,Object>> tableData = new ArrayList<>();
        List<String> modelList = rootMap.entrySet().stream().map(Map.Entry::getKey).distinct().collect(Collectors.toList());
        altNameList.forEach( altName -> {
            modelList.forEach( model -> {
                Map<String, Object> dataMap = MapUtil.builder(new HashMap<String, Object>())
                        .put("alt", altName)
                        .put("model", model)
                        .put("corrCoee", coeeMap.get(model).get(altName))
                        .put("root", rootMap.get(model).get(altName).get("confidenceInterval"))
                        .put("avgAbsolute", absoluteErrorMap.get(model).get(altName).get("confidenceInterval"))
                        .put("avgRelative", relativeErrorMap.get(model).get(altName).get("confidenceInterval"))
                        .build();
                tableData.add(dataMap);
            });
        });
        resultMap.put("tableData", tableData);

        //3. 最佳策略结果
        String solarLevelName = vo.getSolarLevelName(vo.getSolarLevel());
        String magLevelName = vo.getMagLevelName(vo.getMagLevel());

        List<String> bestModelStrategyList = new JSONArray(contentMap.get("bestModel")).toList(String.class);
        List<String> bestModelStr = bestModelStrategyList.stream().map(bestModelStrategy -> {

            //{
            //		"CHAMP": { //在太阳活动高年地磁平静期0-400km高度范围最优模型策略为：
            //			"average_absolute_error": "Jacchia71",  		//评估指标为平均绝对误差时，最优模型为 Jacchia71；
            //			"average_relative_error": "Jacchia71",  		//评估指标为平均相对误差时，最优模型为 Jacchia71；
            //			"average_root_mean_square_error": "Jacchia71", 	//评估指标为均方根误差时，最优模型为 Jacchia71；
            //			"average_corrCoee": "JB2008"					//评估指标为相关系数时，最优模型为 JB2008。
            //		}
            //	}
            Map<String, Map<String, String>> map = JSONUtil.toBean(bestModelStrategy, Map.class);
            return map.entrySet().stream().map(entry -> {
                String altName = entry.getKey();
                Map<String, String> bestModelMap = entry.getValue();
                String prefixStr = "在" + solarLevelName + magLevelName + altName +"高度范围最优模型策略为：\n";

                String suffixStr = bestModelMap.entrySet()
                        .stream()
                        .map(s -> vo.translateErrorType(s.getKey()) + s.getValue())
                        .collect(Collectors.joining(";\n"));
                return prefixStr + suffixStr;
            }).collect(Collectors.toList()).get(0);
        }).collect(Collectors.toList());
        resultMap.put("bestModel", bestModelStr);
        resultMap.put("txtPath", txtPath);


        SseEmitterUtils.sendMessage(sseId,"100");
        SseEmitterUtils.remove(sseId);

        //4. txt文件也要加上
        List<String> list = Stream.of(txtPath).collect(Collectors.toList());
        resultMap.put("resultData", JSONUtil.toJsonStr(list));
        resultMap.put("module", ResultSaveModule.MODEL_ASSESS);
        resultMap.put("submodule", ResultSaveModule.MODEL_ASSESS_DIF_SPACE);
        return AjaxResult.success(resultMap);
    }


    @ApiOperation(value = "建立连接", notes = "建立连接")
    @GetMapping(path = "subscribe", produces = {MediaType.TEXT_EVENT_STREAM_VALUE})
    public SseEmitter push(String id, String flag) {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        return SseEmitterUtils.connect(id+loginUser.getUsername(), flag);
    }


}
